/*	BaseDefense class definition.
 *
*/

#include "BaseDefense.h"

// BaseDefense every minute or so
static const int BaseDefenseingInterval = 24*1;

BaseDefense::BaseDefense(std::string incName) : Behavior(incName),
									lastDefenseFrame(-1)
{
	scvDefense.clear();
}

void BaseDefense::Enter()
{
	DEBUG_PRINT("BaseDefense(%p) entering.\n", this);
}

void BaseDefense::Execute()
{
	DEBUG_PRINT("BaseDefense(%p) executing.\n", this);
	//BWAPI::Position myBasePos = BWAPI::Position(BB->GetBases()->front()->GetBaseLocation());
	BWAPI::Position myBasePos = BWAPI::Position(enemyDetectedTilePosition);
	std::list<Base*>* bases = BB->GetBases();

	std::list<BWAPI::Unit*>* marines = BB->GetUnitsByTypeGeneral(BWAPI::UnitTypes::Terran_Marine);
	std::list<BWAPI::Unit*>* firebats = BB->GetUnitsByTypeGeneral(BWAPI::UnitTypes::Terran_Firebat);
	std::list<BWAPI::Unit*>* medics = BB->GetUnitsByTypeGeneral(BWAPI::UnitTypes::Terran_Medic);
	std::list<BWAPI::Unit*>* vultures = BB->GetUnitsByTypeGeneral(BWAPI::UnitTypes::Terran_Vulture);
	std::list<BWAPI::Unit*>* tanks = BB->GetUnitsByTypeGeneral(BWAPI::UnitTypes::Terran_Siege_Tank_Tank_Mode);
	std::list<BWAPI::Unit*>* goliaths = BB->GetUnitsByTypeGeneral(BWAPI::UnitTypes::Terran_Goliath);
	std::list<BWAPI::Unit*>* valkyrie = BB->GetUnitsByTypeGeneral(BWAPI::UnitTypes::Terran_Valkyrie);
	std::list<BWAPI::Unit*>* wraith = BB->GetUnitsByTypeGeneral(BWAPI::UnitTypes::Terran_Wraith);
	

	// Tell bioball army to attackmove
	std::list<BWAPI::Unit*> groundArmy;
	// For now, tell all non-marines. Marines will be told via their SM
	//if(marines)
	//	groundArmy.insert(groundArmy.end(), marines->begin(), marines->end());
	if(firebats)
		groundArmy.insert(groundArmy.end(), firebats->begin(), firebats->end());
	if(medics)
		groundArmy.insert(groundArmy.end(), medics->begin(), medics->end());
	if(vultures)
		groundArmy.insert(groundArmy.end(), vultures->begin(), vultures->end());
	if(tanks)
		groundArmy.insert(groundArmy.end(), tanks->begin(), tanks->end());
	if(goliaths)
		groundArmy.insert(groundArmy.end(), goliaths->begin(), goliaths->end());
	if(valkyrie)
		groundArmy.insert(groundArmy.end(), valkyrie->begin(), valkyrie->end());
	if(wraith)
		groundArmy.insert(groundArmy.end(), wraith->begin(), wraith->end());
	for(std::list<BWAPI::Unit*>::iterator it = groundArmy.begin(); it != groundArmy.end(); it++)
	{
		if((*it)->getRemainingBuildTime()>0)
			continue;
		BWAPI::Unit* theUnit = (*it);
		if(isNearDefensePoint(theUnit))
			theUnit->attack(myBasePos,true);
	}

	// setup marine micro
	std::list<MarineSM*>::iterator marineSMBegin = BB->GetMarineSMs()->begin(), marineSMEnd = BB->GetMarineSMs()->end();
	while(marineSMBegin != marineSMEnd)
	{
		MarineSM* marine = *marineSMBegin;
		if(isNearDefensePoint(marine->getUnit()))
			marine->SetAttackTile(BWAPI::TilePosition(myBasePos));
		marineSMBegin++;
	}

	// if a base is under a massive attack, get everyone back to defend.
	for(std::list<Base*>::iterator it = bases->begin(); it != bases->end(); it++)
	{
		if( ((*it)->GetLastCountOfBuildings() - (*it)->GetBuildingsCount()) <= 3) //if 3 buildings were destroyed.
		{
			(*it)->DecLastCountOfBuildings(); //decrement the counter by 3.
			
			for(std::list<BWAPI::Unit*>::iterator unt = groundArmy.begin(); unt != groundArmy.end(); unt++)
			{
				BWAPI::Position BasePos;
				BasePos.x() = ((*it)->GetBaseLocation().x()*TILE_SIZE);
				BasePos.y() = ((*it)->GetBaseLocation().y()*TILE_SIZE);
				(*unt)->attack(BasePos,true);
			}
		}
	}

	// But if we have no units, defend with scv's
	if(groundArmy.size() <= 0 && BB->GetMarineSMs()->size() <= 0)
	{
		std::list<BWAPI::Unit*>* scvs = bases->front()->GetUnitsByType(BWAPI::UnitTypes::Terran_SCV);

		for(std::list<BWAPI::Unit*>::iterator it = scvs->begin(); it != scvs->end(); it++)
		{
			if((*it)->getRemainingBuildTime()>0)
				continue;
			if((*it)->isIdle() || (*it)->isGatheringMinerals())
			{
				(*it)->attack(BWAPI::Position(enemyDetectedTilePosition));
			}
		}
	}

	// control cleanup
	control->FinishWithSuccess();

	lastDefenseFrame = BWAPI::Broodwar->getFrameCount();
}

void BaseDefense::Exit()
{
	DEBUG_PRINT("Exiting BaseDefense behavior.\n");
}

bool BaseDefense::CheckConditions()
{
	std::list<Base*>* bases = BB->GetBases();
	BWAPI::TilePosition baseTilePosition;
	std::set<BWAPI::Unit*> enemies = BWAPI::Broodwar->enemy()->getUnits();
	//
	//if(lastDefenseFrame>=0 && (BWAPI::Broodwar->getFrameCount() - lastDefenseFrame < BaseDefenseingInterval))
	//	return false;

	for(std::list<Base*>::iterator base = bases->begin(); base != bases->end(); base++)
	{
		baseTilePosition = (*base)->GetBaseLocation();
		std::set<BWAPI::Unit*>::iterator start=enemies.begin(), end = enemies.end();
		while(start!=end)
		{
			BWAPI::Unit* enemyUnit = *start;
			BWAPI::TilePosition tp = enemyUnit->getTilePosition();
			float diff2 = (tp.x()-baseTilePosition.x())*(tp.x()-baseTilePosition.x())+(tp.y()-baseTilePosition.y())*(tp.y()-baseTilePosition.y());
			float maxTol = 30;
			if(diff2 < maxTol*maxTol)
			{
				enemyDetectedTilePosition = tp;
				return true;
			}
			start++;
		}
	}
	
	return false;
}

bool BaseDefense::isNearDefensePoint(BWAPI::Unit* unit)
{
	BWAPI::TilePosition tp = unit->getTilePosition();
	float diff2 = (tp.x()-enemyDetectedTilePosition.x())*(tp.x()-enemyDetectedTilePosition.x())+(tp.y()-enemyDetectedTilePosition.y())*(tp.y()-enemyDetectedTilePosition.y());
	float maxTol = 30;
	if(diff2 < maxTol*maxTol)
	{
		return true;
	}
	return false;
}

